﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Dynamic.Utils;
using System.Reflection;
using System.Runtime.CompilerServices;

#if CLR2
namespace Microsoft.Scripting.Ast
{
#else
namespace System.Linq.Expressions {
#endif

    public class NewStaticInitExpression : Expression, IArgumentProvider
    {
        private readonly ConstructorInfo _constructor;
        private IList<Expression> _arguments;
        private IList<Type> _paramTypes;

        internal NewStaticInitExpression(ConstructorInfo contructor, IList<Expression> arguments, IList<Type> paramTypes)
        {
            _constructor = contructor;
            _arguments = arguments;
            _paramTypes = paramTypes;
        }

        /// <summary>
        /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
        /// </summary>
        /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
        public override Type Type
        {
            get { return _constructor.DeclaringType; }
        }

        internal IList<Type> ParamTypes
        {
            get { return _paramTypes; }
        }

        /// <summary>
        /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
        /// </summary>
        /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
        public sealed override ExpressionType NodeType
        {
            get { return ExpressionType.NewStaticInit; }
        }

        /// <summary>
        /// Gets the called constructor.
        /// </summary>
        public ConstructorInfo Constructor
        {
            get { return _constructor; }
        }

        /// <summary>
        /// Gets the arguments to the constructor.
        /// </summary>
        public ReadOnlyCollection<Expression> Arguments
        {
            get { return ReturnReadOnly(ref _arguments); }
        }

        #region IArgumentProvider Members

        Expression IArgumentProvider.GetArgument(int index)
        {
            return _arguments[index];
        }

        int IArgumentProvider.ArgumentCount
        {
            get
            {
                return _arguments.Count;
            }
        }

        #endregion

        /// <summary>
        /// Dispatches to the specific visit method for this node type.
        /// </summary>
        protected internal override Expression Accept(ExpressionVisitor visitor)
        {
            return visitor.VisitNew(this);
        }

        /// <summary>
        /// Creates a new expression that is like this one, but using the
        /// supplied children. If all of the children are the same, it will
        /// return this expression.
        /// </summary>
        /// <param name="arguments">The <see cref="Arguments" /> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public NewStaticInitExpression Update(IEnumerable<Expression> arguments)
        {
            if (arguments == Arguments)
            {
                return this;
            }
            return Expression.NewStaticInit(Constructor, arguments,ParamTypes);
        }
    }

    public partial class Expression
    {
        /// <summary>
        /// Creates a new <see cref="NewStaticInitExpression"/> that represents calling the specified constructor that takes no arguments. 
        /// </summary>
        /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
        /// <returns>A <see cref="NewStaticInitExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/> property set to the specified value.</returns>
        public static NewStaticInitExpression NewStaticInit(ConstructorInfo constructor)
        {
            return NewStaticInit(constructor, (IEnumerable<Expression>)null,null);
        }

        /// <summary>
        /// Creates a new <see cref="NewStaticInitExpression"/> that represents calling the specified constructor that takes no arguments. 
        /// </summary>
        /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
        /// <param name="paramTypes">An array of <see cref="Type"/> The Type array of the Arguments collection.</param>
        /// <param name="arguments">An array of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
        /// <returns>A <see cref="NewStaticInitExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/> and <see cref="P:Arguments"/> properties set to the specified value.</returns>
        public static NewStaticInitExpression NewStaticInit(ConstructorInfo constructor, IList<Type> paramTypes,params Expression[] arguments)
        {
            return NewStaticInit(constructor, (IEnumerable<Expression>)arguments, paramTypes);
        }

        /// <summary>
        /// Creates a new <see cref="NewStaticInitExpression"/> that represents calling the specified constructor that takes no arguments. 
        /// </summary>
        /// <param name="constructor">The <see cref="ConstructorInfo"/> to set the <see cref="P:Constructor"/> property equal to.</param>
        /// <param name="arguments">An <see cref="IEnumerable{T}"/> of <see cref="Expression"/> objects to use to populate the Arguments collection.</param>
        /// <param name="paramTypes">An <see cref="IList{T}"/> of <see cref="Type"/> The Type array of the Arguments collection.</param>
        /// <returns>A <see cref="NewStaticInitExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="P:New"/> and the <see cref="P:Constructor"/> and <see cref="P:Arguments"/> properties set to the specified value.</returns>
        public static NewStaticInitExpression NewStaticInit(ConstructorInfo constructor, IEnumerable<Expression> arguments, IList<Type> paramTypes)
        {
            ContractUtils.RequiresNotNull(constructor, "constructor");
            ContractUtils.RequiresNotNull(constructor.DeclaringType, "constructor.DeclaringType");
            TypeUtils.ValidateType(constructor.DeclaringType);
            var argList = arguments.ToReadOnly();
            //ValidateArgumentTypes(constructor, ExpressionType.New, ref argList);

            return new NewStaticInitExpression(constructor, argList, paramTypes);
        }

    }
}
